package moodish.server;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.LinkedList;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import moodish.comm.ServerComm;
import moodish.comm.ServerSideMessage;
import moodish.comm.ServerSideMessage.Type;

/**
 * @author Gon�alo Horta
 * construtor onde � construida a janela do servidor
 *
 */
public class MoodishServerDummy implements MoodishServer, Runnable{

	private static final String DATA_FILE = "data.txt";

	private ServerComm serverComm;
	private boolean isConnected = false;

	private JFrame window;
	private JTextArea textArea;
	private JButton stop_server;

	private LinkedList<Friendship> friendships;
	private LinkedList<User> users;

	public MoodishServerDummy(){
		window = new JFrame ("Moodish Server Dummy");
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		window.setSize(500,400);
		window.getContentPane().setLayout(new BorderLayout());

		textArea = new JTextArea();
		textArea.setBorder(BorderFactory.createTitledBorder("Server"));
		window.getContentPane().add(new JScrollPane(textArea), BorderLayout.CENTER);

		stop_server = new JButton ("Stop");
		stop_server.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e){
				isConnected = false;
				stop_server.setEnabled(false);
			}
		});
		window.getContentPane().add(stop_server, BorderLayout.SOUTH);
		window.setVisible(true);
	
	
	}


	/**
	 * cria��o da thread e inicio da mesma
	 * @param serverComm
	 */
	@Override
	public void start(ServerComm serverComm) {
		this.serverComm = serverComm;
		Thread thread = new Thread(this);
		thread.start();
	}

	/**
	 * metodo que corre quando a thread � executada, que ir� carregar os ficheiros, 
	 * imprimir a lista de users e friendships que existem
	 * iniciar o server communicator e receber as novas mensagens
	 * @author David Vieira
	 */
	@Override
	public void run() {
		/**
		 * carrega os dados do ficheiros
		 */
		friendships = new LinkedList<Friendship>();
		users = new LinkedList<User>();
		Rar rar = loadData();
		if(rar != null) {
			friendships = rar.getFriendships();
			users = rar.getUsers();
		}
		
		/**
		 * imprime lista com os users que existem
		 */
		textArea.append("Loaded Users:\n");
		for (int i = 0; i < users.size(); i++) {
			textArea.append("\t" + users.get(i).toString() + "\n");
		}
		/**
		 * imprime lista com as friendships que existem
		 */
		textArea.append("\n");
		textArea.append("Loaded Friendship:\n");
		for (int i = 0; i < friendships.size(); i++) {
			textArea.append("\t" + friendships.get(i).toString() + "\n" );
		}
		
		textArea.append("\n");
		/**
		 *  o server communicator � iniciado(come�a a receber clientes)
		 */
		serverComm.start();
		isConnected = true;
		textArea.append("Connected...\n");
		
		
		/**
		 * enquanto estiver ligado,se nao tiver mensagens este dorme meio segundo
		*  caso contrario vai buscar a mensagem mais antiga e executa-a
		 */
		try {
			while(isConnected){
				if(!serverComm.hasNextMessage())
					Thread.sleep(5000);
				
				ServerSideMessage message = serverComm.getNextMessage();
				textArea.append(message + "\n");
				execute(message);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		/**
		 * grava os seus dados no ficheiro
		 */
		saveData(new Rar(friendships,users));
		
		/**
		 * Notifica todos os clientes de que vai sair,e imprime que se disconectou
		 */
		for (int i = 0; i < users.size(); i++) {
			serverComm.disconnectClient(users.get(i).getNickname());
		}
		textArea.append("Disconnected...");
		
	}

	/**
	 * Executa uma determinada mensagem recebida, ve o tipo e diz qual a 
	 * accao a tomar
	 * @author David Vieira
	 * @param message
	 */
	
	private void execute(ServerSideMessage message){
		String nickname = message.getClientNickname();
		String payload = message.getPayload();
		Type type = message.getType();
		
		LinkedList<String> friendships = null;
		boolean b = false;
		switch (type) {
		/**
		 * caso em que o cliente est� conectado
		 */
		case CLIENT_CONNECTED:
			User user = new User(nickname);
			/**
			 * Se o utilizador nao existir � adicinado "registar"
			 */
			if(!users.contains(user)){
				users.add(user);
			}else {
				/**
				 * senao vai notificar todos os seus amigos que est� online
				 */
				friendships = getFriendsOf(nickname);
				for (int i = 0; i < friendships.size(); i++) {
					serverComm.sendClientConnected(friendships.get(i), nickname);
				}
				/**
				 * envia a informa��o sobre todos os seus amigos
				 */
				for (int i = 0; i < friendships.size(); i++) {
					if (serverComm.clientIsConnected(friendships.get(i))) {
						serverComm.sendClientConnected(nickname, friendships.get(i));
					}else {
						serverComm.sendClientDisconnected(nickname, friendships.get(i));
					}
				}
			}
			
			break;
			/**
			 * caso em que o cliente se disconectou, ele envia a informa��o que
			 * o cliente se disconectou
			 */
		case CLIENT_DISCONNECTED:
			friendships = getFriendsOf(nickname);
			for (int i = 0; i < friendships.size(); i++) {
				serverComm.sendClientDisconnected(friendships.get(i), nickname);
			}
			break;
			/**
			 * caso exista uma nova friendship, envia pra todos a nova friendship
			 */
		case FRIENDSHIP:
			b=addFriendship(nickname, payload);
			if(b)
				serverComm.sendNewFriendship(payload, nickname);
			
			break;
			/**
			 * caso deixe de existir uma friendship, ele envia pra todos essa informa��o
			 */
			
		case UNFREINDSHIP:
			b= removeFriendship(nickname, payload);
			if(b)
				serverComm.sendNewUnfriendship(payload, nickname);
			
			break;
			
		case MOODISH_MESSAGE:
			friendships = getFriendsOf(nickname);
			for (int i = 0; i < friendships.size(); i++) {
				serverComm.sendMoodishMessage(nickname, friendships.get(i), payload);
			}
			break;
			
		default:
			break;
		}
	}
	

	/**
	 * Devolve lista com os nicknames dos utilizadores dos amigos do ncikname recebido como parametro
	 * @param nickname
	 * @return friends
	 */
	private LinkedList<String> getFriendsOf(String nickname){
		LinkedList<String> friends = new LinkedList<String>();
		for (int i = 0; i < friendships.size(); i++) {
			String otherNickname = friendships.get(i).getOtherNickname(nickname);
			if(otherNickname !=null){
				friends.add(otherNickname);
			}
		}
		return friends;
	}


	private boolean addFriendship(String nickname1,String nickname2){
		Friendship newFriendship = new Friendship ( nickname1,  nickname2);
		User user = new User (nickname2);
		//se o user existir e se n�o houver alguma friendship igual a que se quer adicionar
		if (users.contains(user) && !friendships.contains(newFriendship)){
			friendships.add(newFriendship);
			return true;
		}
		return false;
	}



	/**
	 * se o user existir e se n�o houver alguma friendship igual a que se quer remover
	 * @param nickname1
	 * @param nickname2
	 * @return boolean
	 */
	private boolean removeFriendship (String nickname1, String nickname2){
		Friendship removingFriendship = new Friendship (nickname1,nickname2);
		User user = new User (nickname2);
		if(users.contains(user)&&friendships.contains(removingFriendship )){
			friendships.remove(removingFriendship );
			return true;
		}
		return false;
	}

	/**
	 * recebe um objecto Rar e serializa-o num ficheiro DATA_FILE
	 * @param rar
	 */
	private void saveData(Rar rar){
		ObjectOutputStream output;
		try{
			output = new ObjectOutputStream (new FileOutputStream(DATA_FILE));
			try {
				output.writeObject(rar);
			} finally {
				output.close();
			}
		}catch (FileNotFoundException e){
			e.printStackTrace();
		}catch (IOException e){
			e.printStackTrace();
		}
	}

	/**
	 * Carrega o objecto Rar que se encontra serializado no ficheiro DATA_FILE
	 * @return rar
	 */
	private Rar loadData(){
		Rar rar = null;
		ObjectInputStream input;
		try{
			input = new ObjectInputStream (new FileInputStream(DATA_FILE));
			try{
				rar= (Rar) input.readObject();
			}finally{
				input.close();
			}
		}catch(FileNotFoundException e){
			e.printStackTrace();
		}catch (IOException e){
			e.printStackTrace();
		}catch (ClassNotFoundException e){
			e.printStackTrace();

		}
		return rar;
	}
}
